// Copyright 2019 The rhyme_lph Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// K [author] rhyme_lph
// w [email]  rhymelph@gmail.com
// J [github] https://github.com/rhymelph

import 'dart:async';

import 'package:base_plugin/base_plugin.dart';
import 'package:dio/dio.dart';
import 'package:finance_app/generated/l10n.dart';
import 'package:finance_app/src/common/config.dart';
import 'package:finance_app/src/entities/cpf_contribution_entity.dart';
import 'package:finance_app/src/entities/data_report_entity.dart';
import 'package:finance_app/src/entities/payslips_item_entity.dart';
import 'package:finance_app/src/entities/report_list_item_entity.dart';
import 'package:finance_app/src/entities/suggest_date_entity.dart';
import 'package:finance_app/src/entities/tax_submission_detail_entity.dart';
import 'package:finance_app/src/entities/transaction_entity.dart';
import 'package:finance_app/src/http_service/http_bank_reconciliations_service.dart';
import 'package:finance_app/src/http_service/http_report_service.dart';
import 'package:finance_app/src/http_service/http_setting_service.dart';
import 'package:finance_app/src/mixin/check_plan_mixin.dart';
import 'package:finance_app/src/mixin/report_date_select_mixin.dart';
import 'package:finance_app/src/mixin/report_refresh_mixin.dart';
import 'package:finance_app/src/mixin/scroll_to_hide_mixin.dart';
import 'package:finance_app/src/mixin/search_data_list_mixin.dart';
import 'package:finance_app/src/provider/all_reports/bank_reconciliation/bank_reconciliation_provider.dart';
import 'package:finance_app/src/provider/all_reports/bank_reconciliation/views/view_report_provider.dart';
import 'package:finance_app/src/router_gen/router_gen_providers.dart';
import 'package:finance_app/src/utils/date_time_utils.dart';
import 'package:finance_app/src/utils/file_utils.dart';
import 'package:finance_app/src/utils/reg_exp_utils.dart';
import 'package:finance_app/src/utils/report_utils.dart';
import 'package:finance_app/src/widgets/bottom_sheet/date_time_select_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/delete_info_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/filter_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/got_it_tip_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/normal_info_bottom_sheet.dart';
import 'package:flutter/material.dart';

class DateReportProvider extends BaseProvider<DataReportEntity>
    with
        ReportDateSelectMixin,
        SearchDataListMixin,
        ScrollToHideMixin,
        ReportRefreshMixin,
        CheckPlanMixin {
  List<ReportListItemEntity> reportList = [];
  Map initMeta = {};

  List<String> menuList = ['Open PDF Report'];
  List<String> currentSelectFilterList = [];
  List<String> filterList = [
    // 'Electronic',
    // 'Entertainment',
    // 'No Category Tagged',
  ];

  DateTime asAt;

  String yearOfAssessment = 'YA 2021';

  bool editMode = false;

  // String accountingPeriod = '1 Jan 2020 - 31 Dec 2020';

  List<TaxSubmissionDetailEntity> taxSubmissions;
  List<TaxSubmissionDetailTaxSubmissionLines> lines;
  TaxSubmissionDetailTaxSubmissionLines line;
  TaxSubmissionDetailEntity currentSelectTax;

  @override
  Future<Response> get dataFuture async {
    switch (type) {
      case 1:
        return HttpReportService.salesByPeriod(
          start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
          end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
        );
      case 2:
        return HttpReportService.salesCustomer(
          start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
          end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
        );
      case 3:
        return HttpReportService.salesByPeriod(
          start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
          end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
          contact_id: initMeta['contact_id'],
        );
      case 11:
        return HttpReportService.salesProducts(
          start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
          end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
        );
      case 21:
        await initAsAt();
        return HttpReportService.salesAgedSummary(
          as_at: DateTimeUtils.formatDateTime(asAt, 'yyyy-MM-dd'),
        );
      case 31:
        int contactId;
        if (data == null) {
          contactId = initMeta['contact_id'];
        } else if (currentSelectFilterList.length > 0) {
          final result = ((data.meta as Map)['contacts'] as List).firstWhere(
              (element) =>
                  element['contact_name'] == currentSelectFilterList.first,
              orElse: () => null);
          if (result != null) {
            contactId = currentSelectFilterList.length > 0
                ? result['contact_id']
                : null;
          }
        }
        await initAsAt();
        return HttpReportService.salesAgedDetail(
            as_at: DateTimeUtils.formatDateTime(asAt, 'yyyy-MM-dd'),
            contact_id: contactId);
      case 4:
        return HttpReportService.purchasesByPeriod(
          start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
          end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
        );
      case 5:
        return HttpReportService.purchasesBySupplier(
          start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
          end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
        );
      case 6:
        return HttpReportService.purchasesByPeriod(
          start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
          end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
          contact_id: initMeta['contact_id'],
        );
      case 12:
        return HttpReportService.purchasesByProducts(
          start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
          end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
        );
      case 22:
        await initAsAt();
        return HttpReportService.purchasesByAgedSummary(
          as_at: DateTimeUtils.formatDateTime(asAt, 'yyyy-MM-dd'),
        );
      case 32:
        int contactId;
        if (data == null) {
          contactId = initMeta['contact_id'];
        } else if (currentSelectFilterList.length > 0) {
          final result = ((data.meta as Map)['contacts'] as List).firstWhere(
              (element) =>
                  element['contact_name'] == currentSelectFilterList.first,
              orElse: () => null);
          if (result != null) {
            contactId = currentSelectFilterList.length > 0
                ? result['contact_id']
                : null;
          }
        }
        await initAsAt();
        return HttpReportService.purchasesByAgedDetail(
            as_at: DateTimeUtils.formatDateTime(asAt, 'yyyy-MM-dd'),
            contact_id: contactId);
      case 41:
        await initAsAt();
        return HttpReportService.inventories(
          as_at: DateTimeUtils.formatDateTime(asAt, 'yyyy-MM-dd'),
        );
      case 42:
        return HttpReportService.inventoriesDetail(
            start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
            end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
            product_id: initMeta != null ? initMeta['product_id'] : null);
      case 51:
        try {
          if (taxSubmissions == null) {
            final result = await HttpReportService.taxSubmissions();
            taxSubmissions =
                getEntityFromResult<List<TaxSubmissionDetailEntity>>(result)
                    .reversed
                    .toList();
            currentSelectTax = taxSubmissions.firstWhere(
                (element) => element.status == 'open',
                orElse: () => taxSubmissions.last);
          } else {
            final result = await HttpReportService.taxSubmissions();
            taxSubmissions =
                getEntityFromResult<List<TaxSubmissionDetailEntity>>(result)
                    .reversed
                    .toList();
            final finder = taxSubmissions.firstWhere(
                (element) => element.id == currentSelectTax.id,
                orElse: () => null);
            if (finder != null) {
              currentSelectTax.fromJson(finder.toJson());
            }
          }
          return HttpReportService.taxSubmissionsDetail(currentSelectTax.id);
        } catch (e) {
          print(e);
          return null;
        }
        break;
      case 52:
        try {
          if (taxSubmissions == null) {
            final result = await HttpReportService.taxSubmissions();
            taxSubmissions =
                getEntityFromResult<List<TaxSubmissionDetailEntity>>(result)
                    .reversed
                    .toList();
            if (currentSelectTax == null)
              currentSelectTax = taxSubmissions.first;
          }
          return HttpReportService.taxSubmissionsDetailGST(currentSelectTax.id);
        } catch (e) {
          print(e);
          return null;
        }
        break;
      case 53:
        return HttpReportService.taxSubmissionsLine(
            line.line, currentSelectTax.id);
      case 61:
        try {
          return HttpReportService.taxComputation();
        } catch (e) {
          return null;
        }
        break;
      case 71:
        return HttpReportService.profits(
          start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
          end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
        );
      case 72:
        return HttpReportService.balanceSheets(
          start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
          end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
        );
      case 73:
        return HttpReportService.cashFlow(
          start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
          end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
        );
      case ReportUtils.generalLedger:
        return HttpReportService.generalLedgers(
            start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
            end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
            page: page);
      case 75:
        return HttpReportService.journalEntries(
            start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
            end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
            page: page);
      case 76:
        return HttpReportService.bankReconciliationDetail(initMeta['id']);
      case 77:
        return HttpReportService.fixedAssets();
      case 78:
        return HttpReportService.fixedAssetsHistory();
      case ReportUtils.GeneralLedgerDetail:
        return HttpReportService.generalLedgersDetail(
          coa_account: initMeta['coa_account']?.toString(),
          start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
          end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
        );
      case ReportUtils.payrollMonthlySummary:
        await initMonthListDatePop();
        return HttpReportService.payrollMonthlySummary(
          date: DateTimeUtils.formatDateTime(month, 'yyyy-MM-dd'),
        );
    }
    return null;
  }

  @override
  void initState() {
    super.initState();
    initAsAt();
  }

  void onShowFilter() async {
    unFocus();
    if (type == 31 || type == 32) {
      final result = await Routes.navigateTo(
          RouterGenProviders.ageSelectFilterPage,
          params: {
            'filterList': filterList.toSet().toList(),
            'select': currentSelectFilterList.length > 0
                ? currentSelectFilterList.first
                : null,
          });
      if (result != null) {
        if (result == false) {
          currentSelectFilterList.clear();
        } else {
          currentSelectFilterList.clear();
          currentSelectFilterList.add(result);
        }
        onFilterRefresh();
      }
    } else {
      obtainContext.call((context) async {
        final result = await showReportFilterBottomSheet(
          context,
          selectFilter: currentSelectFilterList,
          defaultFilter: filterList,
        );
        // if (!DeepCollectionEquality().equals(result, currentSelectFilterList)) {
        currentSelectFilterList = result;
        onFilterRefresh();
        // }
      });
    }
  }

  void onFilterRefresh() {
    if (type == 11 || type == 12 || type == 41) {
      filterReportItem();
      update();
    } else {
      onRequestRefresh();
    }
  }

  void filterReportItem({bool needClear}) {
    reportList.clear();
    for (final item in data.body) {
      if (currentSelectFilterList.length == 0) {
        reportList.add(item);
      } else {
        if (item.meta is Map) {
          if (currentSelectFilterList.contains(item.meta['category'])) {
            reportList.add(item);
          }
        } else if (item.meta is List && item.style == 'heading') {
          reportList.add(item);
        } else if (item.meta is List && item.style == 'break') {
          if (reportList.length > 0 && reportList.last.style != 'break') {
            reportList.add(item);
          }
        } else if (item.meta is List &&
            item.style == 'background' &&
            currentSelectFilterList.contains(item.values.first)) {
          reportList.add(item);
        }
      }
    }
  }

  void onRemoveFilter(String value) {
    currentSelectFilterList.remove(value);
    onFilterRefresh();
  }

  void onRequestRefresh() {
    page = 1;
    post(dataFuture,
        autoHandler: AutoHandler.Toast,
        loadingTip: S.current.loading, onSuccess: (result) {
      data =
          transformData(getEntityFromResult<DataReportEntity>(result), false);
      update();
    });
  }

  void onSelectAsAt() {
    searchFocusNode.unfocus();
    obtainContext?.call((context) async {
      final result =
          await showDateTimeSelectBottomSheet(context, S.current.as_at, asAt);
      if (result != null) {
        searchController.clear();
        asAt = result;
        update();
        onRequestRefresh();
      }
    });
  }

  @override
  void searchRefreshData(searchData) {
    if (type == 41) {
      reportList.clear();
      for (final item in data.body) {
        if (item.meta is Map) {
          String contactName = item.meta['name'];
          String number = item.meta['number'];
          if (RegExpUtils.getSearchRegExp(searchData).hasMatch(contactName) ||
              RegExpUtils.getSearchRegExp(searchData).hasMatch(number)) {
            reportList.add(item);
          }
        } else if (item.meta is List && item.style == 'heading') {
          reportList.add(item);
        } else if (item.meta is List && item.style == 'background') {
          reportList.add(item);
        } else if (item.meta is List && item.style == 'break') {
          if (reportList.length > 0 && reportList.last.style != 'break') {
            reportList.add(item);
          }
        }
      }
      update();
    } else {
      reportList.clear();
      for (final item in data.body) {
        if (item.meta is Map) {
          String contactName = item.meta['contact_name'];
          if (RegExpUtils.getSearchRegExp(searchData).hasMatch(contactName)) {
            reportList.add(item);
          }
        } else if (item.meta is List && item.style == 'heading') {
          reportList.add(item);
        } else if (item.meta is List && item.style == 'break') {
          if (reportList.length > 0 && reportList.last.style != 'break') {
            reportList.add(item);
          }
        }
      }
      update();
    }
  }

  int type;

  void initDate(
    int type,
    Map meta,
    TaxSubmissionDetailTaxSubmissionLines line,
    List<TaxSubmissionDetailTaxSubmissionLines> lines,
    List<TaxSubmissionDetailEntity> taxes,
    TaxSubmissionDetailEntity tax,
    bool canEdit,
  ) {
    if (meta != null && meta['currentSelectDatePop'] != null) {
      currentSelectDatePop = meta['currentSelectDatePop'];
    }
    if (meta != null && meta['start_date'] != null) {
      from = DateTimeUtils.toDateTime(meta['start_date'], 'yyyy-MM-dd');
    }
    if (meta != null && meta['end_date'] != null) {
      to = DateTimeUtils.toDateTime(meta['end_date'], 'yyyy-MM-dd');
    }

    if (meta != null && meta['asAt'] != null) {
      asAt = DateTimeUtils.toDateTime(meta['asAt'], 'yyyy-MM-dd');
    }
    this.type = type;

    if (type == 31 || type == 32 || type == 42) {
      menuList = ['Open PDF Report', 'Download Excel'];
    } else if (type == 51) {
      menuList = ['Open PDF Report', 'Mark As Submitted', 'Edit Balance'];
      // menuList = ['Open PDF Report','Record Money-In'];
    } else if (type == 61) {
      menuList = ['Open PDF Report', 'Accept Tax Computation'];
    } else if (type == 75) {
      menuList = ['Open PDF Report', 'Add General Journal'];
    } else if (type == 77) {
      menuList = ['Open PDF Report', 'Add Fixed Asset', 'History'];
    } else if (type == 78) {
      menuList = [];
    }
    if (type == ReportUtils.GeneralLedgerDetail) {
      initMeta = meta;
      from = DateTimeUtils.toDateTime(initMeta['start_date'], 'yyyy-MM-dd');
      to = DateTimeUtils.toDateTime(initMeta['end_date'], 'yyyy-MM-dd');
    } else {
      if (meta != null) {
        currentSelectFilterList.add(meta['contact_name']);
        initMeta = meta;
      }
    }

    if (type == 52) {
      taxSubmissions = taxes;
      currentSelectTax = tax;
    }

    if (type == 53) {
      taxSubmissions = taxes;
      currentSelectTax = tax;
      this.line = line;
      this.lines = lines;
    }

    if (type == 76) {
      menuList = ['Open PDF Report', 'Undo Reconciliation'];
    }
    if (canEdit == true) {
      menuList = [];
    }
  }

  void onSelectMenu(String value) {
    switch (value) {
      case 'History':
        Routes.navigateTo(RouterGenProviders.dateReportPage, params: {
          'type': 78,
        });
        break;
      case 'Mark As Submitted':
        _onMarkAsSubmitted();
        break;
      case 'Record Money-Out':
        _onNavCashOut();
        break;
      case 'Record Money-In':
        _onNavCashIn();
        break;
      case 'Accept Tax Computation':
        _onAcceptTaxComputation();
        break;
      case 'Add General Journal':
      case 'Add Fixed Asset':
        onNavAdd();
        break;
      case 'Open PDF Report':
        onSharePDFReport();
        break;
      case 'Edit Balance':
        onEditBalance();
        break;
      case 'Undo Reconciliation':
        onUndoReconciliation();
        break;
      case 'Download Excel':
        onDownloadExcel();
        break;
    }
  }

  @override
  FutureOr<DataReportEntity> transformData(DataReportEntity d, bool isCache) {
    if (d.body != null && d.body is List) {
      Set<String> categorySet = {};
      for (final item in d.body) {
        if (item.meta is Map) {
          categorySet.add(item.meta['category']);
        }
      }
      if (categorySet.length != 0) {
        if (filterList.length == 0) filterList = categorySet.toList();
      }
      reportList = List<ReportListItemEntity>.from(d.body);
    }

    initMonthListDatePop();
    // else if (d.list?.body != null) {
    //   reportList = List<ReportListItemEntity>.from(d.list.body);
    // }
    if (d.meta != null && d.meta is Map) {
      if (d.meta['contacts'] != null && d.meta['contacts'] is List) {
        // if (filterList.length == 0)
        filterList = (d.meta['contacts'] as List)
            .map((e) => e['contact_name'] as String)
            .toList();
      }
      if (d.meta['line_submission'] != null && type == 61) {
        currentSelectTax = getEntityFromMap<TaxSubmissionDetailEntity>(
            d.meta['line_submission']);
        if (currentSelectTax.status == 'submitted') {
          menuList = [
            'Open PDF Report',
          ];
        } else if (currentSelectTax.status != 'open') {
          menuList = ['Open PDF Report', 'Edit Balance'];
        } else {
          menuList = [
            'Open PDF Report',
            'Edit Balance',
            'Accept Tax Computation'
          ];
        }
      }
    }

    if (type == 51) {
      if (currentSelectTax.status == 'open') {
        menuList = ['Open PDF Report', 'Mark As Submitted', 'Edit Balance'];
      } else if (currentSelectTax.status == 'submitted') {
        if (d.meta['payment_method'] == 'cash_out') {
          menuList = ['Open PDF Report', 'Record Money-Out', 'Edit Balance'];
        } else if (d.meta['payment_method'] == 'cash_in') {
          menuList = ['Open PDF Report', 'Record Money-In', 'Edit Balance'];
        } else {
          menuList = ['Open PDF Report'];
        }
      } else {
        menuList = ['Open PDF Report'];
      }
    }

    return super.transformData(d, isCache);
  }

  //跳转到ageDetail
  void onNavAgeDetail(meta) {
    Routes.navigateTo(RouterGenProviders.dateReportPage, params: {
      'type': type == 21 ? 31 : 32,
      'meta': meta as Map,
    });
  }

  //跳转到公司详情
  void onNavCompanyDetail(ReportListItemEntity item) {
    Routes.navigateTo(RouterGenProviders.dateReportPage, params: {
      'type': type == 2 ? 3 : 6,
      'meta': {
        'contact_id': item.meta['contact_id'],
        'contact_name': item.values[0],
        'currentSelectDatePop': currentSelectDatePop,
        'start_date': from.toIso8601String(),
        'end_date': to.toIso8601String(),
      }
    });
  }

  void onNavAdd() {
    if (isPlanExpired()) return;

    if (type == 75) {
      Routes.navigateTo(RouterGenProviders.newJournalEntryPage);
    } else if (type == 77) {
      Routes.navigateTo(RouterGenProviders.newFixedAssetPage);
    }
  }

  void onChangeSubmission(String value) {
    final finder = taxSubmissions.firstWhere(
        (e) =>
            value ==
            '${DateTimeUtils.formatDateTimeString(e?.startDate)} - ${DateTimeUtils.formatDateTimeString(e?.endDate)}',
        orElse: () => null);
    currentSelectTax = finder;
    onRequestRefresh();
  }

  void onChangeLine(String value) {
    final finder =
        lines.firstWhere((e) => value == e.line.toString(), orElse: () => null);
    line = finder;
    onRequestRefresh();
  }

  void _onMarkAsSubmitted() async {
    if (isPlanExpired()) return;
    bool isSuccess = false;
    await post(HttpReportService.taxSubmissionsSubmitted(currentSelectTax.id),
        autoHandler: AutoHandler.Toast,
        loadingTip: S.current.loading, onSuccess: (result) {
      isSuccess = true;
    });
    if (isSuccess) {
      onRequestRefresh();
    }
  }

  void _onNavCashOut() async {
    if (isPlanExpired()) return;
    final result =
        await Routes.navigateTo(RouterGenProviders.newCashOutPage, params: {
      'type': 'gst',
      'total': data.meta['amount'].toDouble(),
      'taxPeriod': currentSelectTax,
      'isSelectMode': true,
    });
    if (result != null) {
      onRequestRefresh();
    }
  }

  void _onNavCashIn() async {
    if (isPlanExpired()) return;
    final result =
        await Routes.navigateTo(RouterGenProviders.newCashInPage, params: {
      'type': 'gst',
      'total': data.meta['amount'].toDouble(),
      'taxPeriod': currentSelectTax
    });
    if (result != null) {
      onRequestRefresh();
    }
  }

  void onNavGSTLineDetail(ReportListItemEntity item) {
    if (item.values[0] == '9' ||
        item.values[0] == '10' ||
        item.values[0] == '11' ||
        item.values[0] == '12' ||
        item.values[0] == '14' ||
        item.values[0] == '15' ||
        item.values[0] == '17' ||
        item.values[0] == '18' ||
        item.values[0] == '19') return;

    if (item.values[0] == '8' || item.values[0] == '16') {
      Routes.navigateTo(RouterGenProviders.dateReportPage, params: {
        'type': 52,
        'meta': data.meta as Map,
        'taxes': taxSubmissions,
        'tax': currentSelectTax,
      });
    } else if (item.values[0] == '13') {
      Routes.navigateTo(RouterGenProviders.dateReportPage, params: {
        'type': 71,
        'meta': {
          'currentSelectDatePop': S.current.custom,
          'start_date': currentSelectTax.startDate,
          'end_date': currentSelectTax.endDate,
        },
        'line': currentSelectTax.lines.firstWhere(
            (element) => element.line == int.parse(item.values[0]),
            orElse: () => null),
        'tax': currentSelectTax,
      });
    } else {
      Routes.navigateTo(RouterGenProviders.dateReportPage, params: {
        'type': 53,
        'meta': data.meta as Map,
        'line': currentSelectTax.lines.firstWhere(
            (element) => element.line == int.parse(item.values[0]),
            orElse: () => null),
        //当前选中的line
        'lines': currentSelectTax.lines,
        'taxes': taxSubmissions,
        'tax': currentSelectTax,
      });
    }
  }

  void onNavInventoryDetail(ReportListItemEntity item) {
    Routes.navigateTo(RouterGenProviders.dateReportPage, params: {
      'type': 42,
      'meta': item.meta as Map,
    });
  }

  void _onAcceptTaxComputation() async {
    if (isPlanExpired()) return;
    bool isSuccess = false;
    await post(HttpReportService.taxComputationAccept(currentSelectTax.id),
        loadingTip: 'Loading....',
        autoHandler: AutoHandler.Toast, onSuccess: (result) {
      isSuccess = true;
    });
    if (isSuccess == true) {
      obtainContext?.call((context) async {
        await showTaxPayableRecordedTipBottomSheet(context);
        onTry();
      });
    }
  }

  void onCancelAccept() {
    obtainContext?.call((context) {
      Navigator.of(context).pop();
    });
  }

  void onAccept() {
    obtainContext?.call((context) {
      showTaxPayableHasBeenRecordedNormalInfoBottomSheet(context);
    });
  }

  void onSharePDFReport() async {
    Future<Response> getPDFReportUrl() async {
      switch (type) {
        case ReportUtils.salesByPeriod:
          return HttpReportService.salesByPeriod(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              is_pdf: 1);
        case ReportUtils.salesByCustomer:
          return HttpReportService.salesCustomer(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              is_pdf: 1);
        case 3:
          return HttpReportService.salesByPeriod(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              contact_id: initMeta['contact_id'],
              is_pdf: 1);
        case ReportUtils.salesByProductAndService:
          return HttpReportService.salesProducts(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              is_pdf: 1);
        case 21:
          return HttpReportService.salesAgedSummary(
              as_at: DateTimeUtils.formatDateTime(asAt, 'yyyy-MM-dd'),
              is_pdf: 1);
        case 31:
          int contactId;
          if (data == null) {
            contactId = initMeta['contact_id'];
          } else if (currentSelectFilterList.length > 0) {
            final result = ((data.meta as Map)['contacts'] as List).firstWhere(
                (element) =>
                    element['contact_name'] == currentSelectFilterList.first,
                orElse: () => null);
            if (result != null) {
              contactId = currentSelectFilterList.length > 0
                  ? result['contact_id']
                  : null;
            }
          }
          return HttpReportService.salesAgedDetail(
              as_at: DateTimeUtils.formatDateTime(asAt, 'yyyy-MM-dd'),
              contact_id: contactId,
              is_pdf: 1);
        case 4:
          return HttpReportService.purchasesByPeriod(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              is_pdf: 1);
        case 5:
          return HttpReportService.purchasesBySupplier(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              is_pdf: 1);
        case 6:
          return HttpReportService.purchasesByPeriod(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              contact_id: initMeta['contact_id'],
              is_pdf: 1);
        case 12:
          return HttpReportService.purchasesByProducts(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              is_pdf: 1);
        case 22:
          return HttpReportService.purchasesByAgedSummary(
            as_at: DateTimeUtils.formatDateTime(asAt, 'yyyy-MM-dd'),
            is_pdf: 1,
          );
        case 32:
          int contactId;
          if (data == null) {
            contactId = initMeta['contact_id'];
          } else {
            final result = ((data.meta as Map)['contacts'] as List).firstWhere(
                (element) =>
                    element['contact_name'] == currentSelectFilterList.first,
                orElse: () => null);
            if (result != null) {
              contactId = currentSelectFilterList.length > 0
                  ? result['contact_id']
                  : null;
            }
          }
          return HttpReportService.purchasesByAgedDetail(
              as_at: DateTimeUtils.formatDateTime(asAt, 'yyyy-MM-dd'),
              contact_id: contactId,
              is_pdf: 1);
        case 41:
          return HttpReportService.inventories(
              as_at: DateTimeUtils.formatDateTime(asAt, 'yyyy-MM-dd'),
              is_pdf: 1);
        case 42:
          return HttpReportService.inventoriesDetail(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              product_id: initMeta != null ? initMeta['product_id'] : null,
              is_pdf: 1);
        case 51:
          try {
            return HttpReportService.taxSubmissionsDetail(currentSelectTax.id,
                is_pdf: 1);
          } catch (e) {
            return null;
          }
          break;
        case 52:
          try {
            return HttpReportService.taxSubmissionsDetailGST(
                currentSelectTax.id,
                is_pdf: 1);
          } catch (e) {
            return null;
          }
          break;
        case 53:
          return HttpReportService.taxSubmissionsLine(
              line.line, currentSelectTax.id,
              is_pdf: 1);
        case 61:
          // if (taxSubmissions == null) {
          //   final result = await HttpReportService.taxSubmissions();
          //   taxSubmissions =
          //       getEntityFromResult<List<TaxSubmissionDetailEntity>>(result);
          //   currentSelectTax = taxSubmissions.first;
          // }
          return HttpReportService.taxComputation(is_pdf: 1);
          break;
        case 71:
          return HttpReportService.profits(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              is_pdf: 1);
        case 72:
          return HttpReportService.balanceSheets(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              is_pdf: 1);
        case 73:
          return HttpReportService.cashFlow(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              is_pdf: 1);
        case 74:
          return HttpReportService.generalLedgers(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              is_pdf: 1);
        case 75:
          return HttpReportService.journalEntries(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              is_pdf: 1);
        case 76:
          return HttpReportService.bankReconciliationDetail(initMeta['id'],
              is_pdf: 1);
        case 77:
          return HttpReportService.fixedAssets(
            is_pdf: 1,
          );
        case 78:
          return HttpReportService.fixedAssetsHistory(
            is_pdf: 1,
          );
        case ReportUtils.GeneralLedgerDetail:
          return HttpReportService.generalLedgersDetail(
            coa_account: initMeta['coa_account'],
            start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
            end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
            is_pdf: 1,
          );
        case ReportUtils.payrollMonthlySummary:
          return HttpReportService.payrollMonthlySummary(
            date: DateTimeUtils.formatDateTime(month, 'yyyy-MM-dd'),
            is_pdf: 1,
          );
      }
      return null;
    }

    obtainContext?.call((context) async {
      BaseConfig.dialogBase
          .showLoadingDialog(context: context, text: S.current.loading);
      try {
        final result = await getPDFReportUrl();
        final file = await FileUtils.saveFile(result, 'pdf');
        if (file != null) {
          print('open File');
          FileUtils.openFile(file);
        } else {
          print('open failure');
        }
      } catch (e) {}
      Navigator.of(context).pop();
    });
  }

  void onNavReportDetail(ReportListItemEntity item) {
    if (item.meta is Map) {
      //asset_depreciation
      print(item.meta);
      if (item.meta['goto'] == 'invoice') {
        int id = int.parse(item.meta['goto_id'].toString());
        Routes.navigateTo(RouterGenProviders.newInvoicePage,
            params: {'data': TransactionEntity()..transactionableId = id});
      } else if (item.meta['goto'] == 'bill') {
        int id = int.parse(item.meta['goto_id'].toString());
        Routes.navigateTo(RouterGenProviders.newBillPage,
            params: {'data': TransactionEntity()..transactionableId = id});
      } else if (item.meta['goto'] == 'gl_detail') {
        item.meta['data']['currentSelectDatePop'] = currentSelectDatePop;
        Routes.navigateTo(RouterGenProviders.dateReportPage, params: {
          'type': ReportUtils.GeneralLedgerDetail,
          'meta': item.meta['data']
        });
      } else if (item.meta['goto'] == 'expense') {
        int id = int.parse(item.meta['goto_id'].toString());
        Routes.navigateTo(RouterGenProviders.newExpensePage,
            params: {'data': TransactionEntity()..transactionableId = id});
      } else if (item.meta['goto'] == 'asset') {
        int id = int.parse(item.meta['goto_id'].toString());
        Routes.navigateTo(RouterGenProviders.newFixedAssetPage, params: {
          'id': id,
        });
      } else if (item.meta['goto'] == 'cash_in') {
        int id = int.parse(item.meta['goto_id'].toString());
        Routes.navigateTo(RouterGenProviders.newCashInPage,
            params: {'data': TransactionEntity()..transactionableId = id});
      } else if (item.meta['goto'] == 'cash_out') {
        int id = int.parse(item.meta['goto_id'].toString());
        Routes.navigateTo(RouterGenProviders.newCashOutPage,
            params: {'data': TransactionEntity()..transactionableId = id});
      } else if (item.meta['goto'] == 'inventory') {
        Routes.navigateTo(RouterGenProviders.inventoryAdjustmentPage,
            params: {'id': item.meta['goto_id']});
      } else if (item.meta['goto'] == 'credit_note') {
        int id = int.parse(item.meta['goto_id'].toString());
        Routes.navigateTo(RouterGenProviders.newCreditNotePage,
            params: {'data': TransactionEntity()..transactionableId = id});
      } else if (item.meta['goto'] == 'debit_note') {
        int id = int.parse(item.meta['goto_id'].toString());
        Routes.navigateTo(RouterGenProviders.newDebitNotePage,
            params: {'data': TransactionEntity()..transactionableId = id});
      } else if (item.meta['goto'] == 'payslip') {
        int id = int.parse(item.meta['goto_id'].toString());
        Routes.navigateTo(RouterGenProviders.payrollDetailPage,
            params: {'data': PayslipsItemEntity()..id = id});
      } else if (item.meta['goto'] == 'cpf_contribution') {
        int id = int.parse(item.meta['goto_id'].toString());
        Routes.navigateTo(RouterGenProviders.recordCpfContributionPage,
            params: {'data': CpfContributionEntity()..id = id});
      } else if (item.meta['page'] == 'journal') {
        final id =
            int.parse(item.meta['0']['id']?.toString(), onError: (e) => null);
        Routes.navigateTo(RouterGenProviders.newJournalEntryPage,
            params: {'id': id, 'title': item.values.last});
      } else if (item.meta['page'] == 'bill') {
        final id =
            int.parse(item.meta['0']['id']?.toString(), onError: (e) => null);
        Routes.navigateTo(RouterGenProviders.newBillPage,
            params: {'data': TransactionEntity()..transactionableId = id});
      } else if (item.meta['page'] == 'payslip') {
        // Routes.navigateTo(RouterGenProviders., params: {
        //   'data': TransactionEntity()..transactionableId = item.meta['0']['id']
        // });
      } else if (item.meta['contact_id'] != null) {
        item.meta['asAt'] = DateTimeUtils.formatDateTime(asAt, 'yyyy-MM-dd');
        onNavAgeDetail(item.meta);
      }
    }
  }

  void onEditBalance() async {
    if (isPlanExpired()) return;
    //修改Balance
    if (type == 51) {
      editMode = true;
      update();
      return;
    }

    final result =
        await Routes.navigateTo(RouterGenProviders.dateReportPage, params: {
      'type': 61,
      'canEdit': true,
    });
    if (result == true) {
      onTry();
    }
  }

  Map<int, String> updateOldMeta = {};

  void onUpdateValue(ReportListItemEntity entity, String data) {
    if (entity.meta is Map && entity.meta['goto_id'] != null) {
      if (data == entity.values.last) return;
      int id = entity.meta['goto_id'];
      if (!updateOldMeta.containsKey(id)) {
        updateOldMeta[id] = entity.values.last;
      } else if (updateOldMeta[id] == entity.values.last) {
        updateOldMeta.remove(id);
      }
      entity.values.last = data;
      // update();
    }
  }

  void onUpdateLineValue(ReportListItemEntity entity, String data) {
    int id = int.parse(entity.values.first);

    updateOldMeta[id] = data;

    if (data == null || data == '') {
      updateOldMeta.remove(id);
    }

    print(updateOldMeta);
    // update();
  }

  void onSave() async {
    obtainContext?.call((context) {
      FocusScope.of(context).requestFocus(FocusNode());
    });
    print("updateOldMeta $updateOldMeta");
    if (type == 51 && updateOldMeta.isNotEmpty) {
      print("here");
      List lines = [];
      for (final item in updateOldMeta.entries) {
        lines
            .add({'id': item.key, 'amount': num.parse(item.value, (e) => 0.0)});
      }
      print(lines);
      bool isSuccess = false;
      await post(HttpReportService.gstF5Balance(currentSelectTax.id, lines),
          autoHandler: AutoHandler.Toast,
          loadingTip: S.current.loading, onSuccess: (result) {
        isSuccess = true;
      });
      if (isSuccess == true) {
        editMode = false;
        obtainContext?.call((context) async {
          await showUpdateBalancesSuccessTipBottomSheet(context);
          Navigator.of(context).pop(true);
        });
      }
    } else if (type == 61 && updateOldMeta.isNotEmpty) {
      List lines = [];
      for (final item in updateOldMeta.entries) {
        final finder = data.body.firstWhere((element) =>
            element.meta is Map && element.meta['goto_id'] == item.key);
        lines.add({
          'id': finder.meta['goto_id'],
          'amount': num.parse(finder.values.last, (e) => 0.0)
        });
      }
      print(lines);
      bool isSuccess = false;
      await post(
          HttpReportService.taxSubmissionsBalance(currentSelectTax.id, lines),
          autoHandler: AutoHandler.Toast,
          loadingTip: S.current.loading, onSuccess: (result) {
        isSuccess = true;
      });
      if (isSuccess == true) {
        obtainContext?.call((context) async {
          await showUpdateBalancesSuccessTipBottomSheet(context);
          Navigator.of(context).pop(true);
        });
      }
    }
  }

  void onUndoReconciliation() {
    if (isPlanExpired()) return;
    obtainContext?.call((context) async {
      bool isSuccess = await showUndoReconciliationBottomSheet(context);
      if (isSuccess == true) {
        isSuccess = false;
        await post(
            HttpBankReconciliationsService.unDoReconcile(
              initMeta['id'],
            ),
            autoHandler: AutoHandler.Toast,
            loadingTip: S.current.loading, onSuccess: (result) {
          isSuccess = true;
        });
        if (isSuccess == true) {
          await showReconciliationSuccessTipBottomSheet(context);
          Providers.get<BankReconciliationProvider>(
                  RouterGenProviders.bankReconciliationPage, null)
              ?.getChildProvider<ViewReportProvider>()
              ?.onRefreshReport();
          Navigator.of(context).pop();
        }
      }
    });
  }

  void onDownloadExcel() {
    print('download excel:$type');

    Future<Response> getExcelReportUrl() async {
      switch (type) {
        case 31:
          int contactId;
          if (data == null) {
            contactId = initMeta['contact_id'];
          } else if (currentSelectFilterList.length > 0) {
            final result = ((data.meta as Map)['contacts'] as List).firstWhere(
                (element) =>
                    element['contact_name'] == currentSelectFilterList.first,
                orElse: () => null);
            if (result != null) {
              contactId = currentSelectFilterList.length > 0
                  ? result['contact_id']
                  : null;
            }
          }
          return HttpReportService.salesAgedDetail(
            as_at: DateTimeUtils.formatDateTime(asAt, 'yyyy-MM-dd'),
            contact_id: contactId,
            is_excel: 1,
          );
        case 32:
          int contactId;
          try {
            if (data == null) {
              contactId = initMeta['contact_id'];
            } else {
              final result = ((data.meta as Map)['contacts'] as List)
                  .firstWhere(
                      (element) =>
                          element['contact_name'] ==
                          currentSelectFilterList.first,
                      orElse: () => null);
              if (result != null) {
                contactId = currentSelectFilterList.length > 0
                    ? result['contact_id']
                    : null;
              }
            }
          } catch (e) {
            print(e);
          }
          return HttpReportService.purchasesByAgedDetail(
              as_at: DateTimeUtils.formatDateTime(asAt, 'yyyy-MM-dd'),
              contact_id: contactId,
              is_excel: 1);
        case 42:
          return HttpReportService.inventoriesDetail(
              start_date: DateTimeUtils.formatDateTime(from, 'yyyy-MM-dd'),
              end_date: DateTimeUtils.formatDateTime(to, 'yyyy-MM-dd'),
              product_id: initMeta != null ? initMeta['product_id'] : null,
              is_excel: 1);
      }
      return null;
    }

    obtainContext?.call((context) async {
      BaseConfig.dialogBase
          .showLoadingDialog(context: context, text: S.current.loading);
      try {
        final future = getExcelReportUrl();
        if (future != null) {
          final result = await future;
          final file = await FileUtils.saveFile(result, 'xls');
          if (file != null) {
            print('open File');
            FileUtils.openFile(file);
          } else {
            print('open failure');
          }
        } else {
          print('not found type = $type');
        }
      } catch (e) {}
      Navigator.of(context).pop();
    });
  }

  Future<void> initAsAt() async {
    if (asAt != null) return;
    String dateType;
    if (type == 21 || type == 31) {
      dateType = 'aged_receivables';
    } else if (type == 22 || type == 32) {
      dateType = 'aged_payables';
    } else if (type == 41) {
      dateType = 'inventory_valuation_summary';
    }
    if (dateType != null) {
      await post(HttpSettingService.getSuggestTime(dateType),
          onSuccess: (result) {
        print("onSuccess asat");
        SuggestDateEntity suggestDateEntity =
            getEntityFromResult<SuggestDateEntity>(result);
        final dateString = suggestDateEntity.typeDate ??
            suggestDateEntity.financialYearDate.start;
        asAt = DateTime.tryParse(dateString).toLocal() ??
            DateTime.parse(Config.currentFY.endDate);
        update();
      });
    } else {
      asAt = DateTime.parse(Config.currentFY.endDate);
    }
  }

  onCancelEdit() {
    updateOldMeta = {};
    editMode = false;
    update();
  }

  @override
  Future get refreshFuture => dataFuture;

  @override
  DataReportEntity get reportData => data;

  @override
  DataReportEntity getFromRequestResult(Response result, bool isRefresh) {
    reportList = List<ReportListItemEntity>.from(reportData.body);
    return getEntityFromResult(result);
  }
}
